package com.qingzhu.component.lock.common.generator;

import com.qingzhu.component.lock.common.cache.LockCache;
import com.qingzhu.component.lock.common.cache.expire.strategy.CacheOutStrategy;
import com.qingzhu.component.lock.common.constant.cache.strategy.LockExpireStrategyTypeConstant;
import com.qingzhu.component.lock.common.entity.LockEntity;
import com.qingzhu.component.lock.common.pool.ForkLockExecutor;
import com.qingzhu.component.lock.common.util.BaseUtil;
import lombok.Data;

/**
 * 抽象的锁生成器
 * @author xiangjz
 * @version 1.0
 * @date 2021/1/14 15:25
 */
@Data
public abstract class LockGenerator<K> {

    // 锁对象缓存
    private final LockCache lockCache;
    // 锁对象缓存淘汰策略
    private final CacheOutStrategy cacheOutStrategy;
    // fork业务线程的限制器
    private final ForkLockExecutor forkLockExecutor;

    public LockGenerator(CacheOutStrategy cacheOutStrategy,
                         ForkLockExecutor forkLockExecutor) {
        this.cacheOutStrategy = cacheOutStrategy;
        this.lockCache = cacheOutStrategy.getLockCache();
        this.forkLockExecutor = forkLockExecutor;
    }

    /**
     * 根据业务key获取锁
     * @param key
     * @return
     */
    public LockEntity getLock(K key) {
        return this.getLock(key, -1L, LockExpireStrategyTypeConstant.LOCK_EXPIRE_STRATEGY_IGNORE, -1L);
    }

    /**
     * 根据业务key获取锁
     * @param key
     * @return
     */
    public LockEntity getLock(K key, long expireOnForkingFull) {
        return this.getLock(key, -1L, LockExpireStrategyTypeConstant.LOCK_EXPIRE_STRATEGY_IGNORE, expireOnForkingFull);
    }

    /**
     * 根据业务key获取锁
     * @param key
     * @return
     */
    public LockEntity getLock(K key, long expire, int strategyOnExpired, long expireOnForkingFull) {

        LockEntity lockEntity = this.lockCache.get(key);
        if(BaseUtil.isEmpty(lockEntity)) {
            lockEntity = this.doGetLock(key, expire);
            lockEntity.setForkLockExecutor(this.forkLockExecutor);
            lockEntity.setExpireOnForkingFull(expireOnForkingFull);
            this.lockCache.cacheIfAbsent(key, lockEntity);
        } else {
            lockEntity.setCurrentThread();
        }
        lockEntity.setStrategyOnExpired(strategyOnExpired);
        this.cacheOutStrategy.check(key);
        return lockEntity;
    }

    public abstract LockEntity doGetLock(K key, long expire);

}
